#include "ethernet.h"
#include "utils.h"
#include "driver.h"
#include "arp.h"
#include "ip.h"
#include <string.h>
#include <stdio.h>

/**
 * @brief 处理一个收到的数据包
 *        你需要判断以太网数据帧的协议类型，注意大小端转换
 *        如果是ARP协议数据包，则去掉以太网包头，发送到arp层处理arp_in()
 *        如果是IP协议数据包，则去掉以太网包头，发送到IP层处理ip_in()
 * 
 * @param buf 要处理的数据包
 */
void ethernet_in(buf_t *buf)
{
    // TODO
    ether_hdr_t* head = (ether_hdr_t*)buf->data;
    if(head->protocol == swap16(NET_PROTOCOL_ARP)){
        buf_remove_header(buf, sizeof(ether_hdr_t));
        arp_in(buf);
    }else if(head->protocol == swap16(NET_PROTOCOL_IP)){
        buf_remove_header(buf, sizeof(ether_hdr_t));
        ip_in(buf);
    }
    // uint8_t *p = (buf->data + 12);
    // uint16_t type = *((uint16_t*)p);
    // // printf("\nbefore type: 0:%x 1:%x = %04x\n", *p, *(p+1), type);
    // type = swap16(type);
    // // printf("\nafter type: 0:%x 1:%x = %04x\n", *p, *(p+1), type);
    // buf_remove_header(buf, 14);
    // // printf("type: 0:%x 1:%x\n", *p, *(p+1));
    // // printf("type = %x\n", type);
    // if (type == NET_PROTOCOL_IP)
    //     ip_in(buf);
    // else if(type == NET_PROTOCOL_ARP)
    //     arp_in(buf);
    
}

/**
 * @brief 处理一个要发送的数据包
 *        你需添加以太网包头，填写目的MAC地址、源MAC地址、协议类型
 *        添加完成后将以太网数据帧发送到驱动层
 * 
 * @param buf 要处理的数据包
 * @param mac 目标mac地址
 * @param protocol 上层协议
 */
void ethernet_out(buf_t *buf, const uint8_t *mac, net_protocol_t protocol)
{
    // TODO
    buf_add_header(buf, 14);
    ether_hdr_t* head = (ether_hdr_t*)buf->data;
    memcpy(head->dest, mac, NET_MAC_LEN);
    memcpy(head->src, net_if_mac, NET_MAC_LEN);
    head->protocol = swap16(protocol);
    // uint8_t *p = buf->data+12;
    // uint8_t l = protocol>>8, r = protocol&0xFF;
    // // *p = r, *(p+1) = l;
    // *p = l, *(p+1) = r;
    driver_send(buf);
        
}

/**
 * @brief 初始化以太网协议
 * 
 * @return int 成功为0，失败为-1
 */
int ethernet_init()
{
    buf_init(&rxbuf, ETHERNET_MTU + sizeof(ether_hdr_t));
    return driver_open();
}

/**
 * @brief 一次以太网轮询
 * 
 */
void ethernet_poll()
{
    if (driver_recv(&rxbuf) > 0)
        ethernet_in(&rxbuf);
}
